iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 2
3
自我挑戰組

Golang魔法使 ─ 30天從零開始學習Go語言 | 比Python還簡單 | 理科生一定學得會 | 文科生不好說系列 第 2 篇

#2 零與壹的魔法世界 ── 宣告數字型態的變數 | Golang魔法使

  • 分享至 

  • xImage
  •  

#2 零與壹的魔法世界 ── 宣告數字型態的變數 | Golang魔法使

前言

昨天小櫻戰鬥的事被好友「大道寺知世」用 V8 偷拍的一清二礎,你在偷偷搜集 Golang 牌的事,已經被發現了,知世也想要變強,他想要得到石鬼面變成不死之身 如今只能把知世加入戰隊一起奮鬥了

如何表達整數?

以「電腦科學」的角度來出發,電腦是怎麼表達數字的,大家都知道魔仗(電腦)內部是由 0 和 1 來運作的,那怎麼表示整數呢?

  • 0 0 0 0 0 0 0 0
  • 0 0 0 0 0 0 0 1
  • 0 0 0 0 0 0 1 0
  • 0 0 0 0 0 0 1 1
  • 0 0 0 0 0 1 0 0
  • ...

其中一個存取 0 或 1 的單元我們稱作「位元」(bit),而每個例子中都有 8bits,根據高一下數學課教的排列組合計數原理,這 8bits,一共可以有 256 排列


10000000 (-128)
10000001 (-127)
10000010 (-126)
...
11111111 (-1)
00000000 (0)
00000001 (1)
00000010 (2)
...
01111110 (126)
01111111 (127)

在 Golang 牌中我們稱這個叫int8,可以用來表示(-128~127)

我們有時並不會用到負號,這時我們記作 uint8 可以表示(0~255)

00000000 (0)
00000001 (1)
00000010 (2)
...
10000000 (128)
10000001 (129)
...
11111111 (255)

uint 只能是正數,int 可以有負數

int 表示負數的存法在電腦科學中稱為「二補數」

Golang上宣告整數變數

其中 int 即為英文的 integer 而 uint 則為 unsigned integer 表示是「無符號的」整數,可能有點難記為什麼不乾脆用 i8 和 u8 呢?這問題很好,Rust 就是這麼認為的,因此 Rust 採用 i8, u8 來表示

package main
import "fmt"
func main(){
    var num int8
    num = 127
    fmt.Print(num)
}

執行結果:
127

首先我們先來看第 4 行 var num int8,var 是宣告變數的起手示,全稱為 variable,什麼是宣告變數呢?就是告訴編譯器,我需要使用一個變數叫作 num 而他是一個 int8 的型態

而這個num的名字由各位魔法使自己決定,變數名稱由數字、英文字母及下劃線組合而成,其中要注意不可以用數字當變數名稱開頭,相信資深工具人一定覺得理所當然,另外,並不是所有單字的可以當變數,有些名字是不能當成變數名稱的,比如若用var當變數名稱,想就知道會出事吧?

而第 5 行 num = 127 則是告訴編譯器 num 是 127。請注意,這裡的等號是一種給予的表現,並不是數學上的等於,給予的方向是左邊接收右邊給予

第 6 行 fmt.Print(num) 則是告訴魔仗要他印出 num,若改使用 fmt.Println(num) 則魔仗則會在印出 num 後再印出換行符號(ln 代表 line) 很多語言也有採類似用法如 java, rust

然而 int8 實在是太短了,如果不小心給到 128 魔仗還會當場給我噴錯,氣氣氣氣氣,這種情況我們稱為 overflow 又稱為溢位

關於溢位

Golang牌在整數上一共給了以下這些型態

int8, uint8
int16, uint16
int32, uint32
int64, uint64
int , uint (大部分情況視為 int64, uint64)

另外大家可能會發現怎麼都是 8 的倍數,因為電腦會以 8 個 bits 做操作,所以又稱 8bits 為 1byte (1個位元組)

更簡短地宣告整數變數

然而,每次都要打 var ooo int ooo = xxx 時在太麻煩了所以又出現了幾種簡單的打法

package main
import "fmt"
func main(){
    var num1 int = 10
    num2 := 20
    fmt.Println(num1)
    fmt.Println(num2)
}

執行結果:
10
20

其中要注第5行 num2所採用的方法是由魔仗自行判定型態,在這裡會直接把 num2 視為 int

注意,若使用 fmt.Print() 則會印出 1020 因為魔仗在用 fmt.Print() 時不會自動在10後面換一行

補充:
Short Variable Declaration Operator(:=) in Go

Golang上宣告浮點數變數(小數)

你一定會很好奇魔仗要怎麼處理小數,現在的魔法基本上都是按照 IEEE754 來處理浮點數的,這部分比較複雜,簡單來說就是將一串浮點數拆成科學記號來表示,當然實際上沒有那麼簡單所以在此就不細講了。我們只要知道怎麼用就行了,在 Golang 中一共有兩種小數的宣告方法,分別為

  • float32
  • float64

因為對電腦來說,考慮到效能,無法完美處理小數,所以有兩種提供選擇。其中float64 花了2倍的空間所以存的比 float32 還要精準,通常我們會稱 float64 為雙精度浮點數

雙精度符點數
補充:有些語言會以 float 代表 float32, double 代表 float64,但是如果是採用 32, 64 這種表示法,就可以很清礎的了解該變數到底占用了多少空間,而且少背一個單字,真的很不錯

整數的運算

加減乘除

通常加減乘除是一定要有的,我們以整數來舉例

package main
import "fmt"
func main(){
    n := 10
    m := 20
    fmt.Println(n + m)
    fmt.Println(n - m)
    fmt.Println(n * m)
    fmt.Println(m / n)
    fmt.Println(n / m)
}

執行結果
30
-10
200
2
0

在絕大多數的語言中,整數對整數運算出來的都是整數,即使是除法也不例外,n / m 是 10/20 在數學中應為 0.5 但在電腦科學中,會直接當作 0

那麼如果有負數呢?比如 -3/2 = -1.5 會變 -2 還是 -1 呢?其實不用像數學什麼 高斯符號 那麼麻煩,只要把小數位去掉就行了,所以會是 -1

package main
import "fmt"
func main(){
    fmt.Println(-3/2)
}

執行結果
-1

取餘

另外,在電腦科學中,有一種計算方式叫作「取餘數」,用「%」當做記號,這裡的「%」跟一般數學、物理、化學在用的「百分比」不一樣,他是一種「取餘數的」計算方式,舉個例子:

30 % 2 會是 0 因為 30/2 = 15...0
29 % 3 會是 2 因為 29/3 = 9...2
23 % 5 會是 3 因為 23/5 = 4...3

package main
import "fmt"
func main(){
    fmt.Println(30 % 2)
    fmt.Println(29 % 3)
    fmt.Println(23 % 5)
}

執行結果
0
2
3

如果是有負數的情況倒是比較少見,但也一樣試用

package main
import "fmt"
func main(){
    fmt.Println(-24 % 7)
    fmt.Println(24 % -7)
    fmt.Println(-24 % -7)
}

執行結果
-3
3
-3

-24 / 7 = -3...-3
(照理說應該也可以 -4...4)
24 / -7 = -3...3
(照理說應該也可以 -4...-4)
-24 / -7 = 3...-3
(照理說應該也可以 4...4)

經過以上觀察,能發現規律,當有負號參與時,取餘的值的正負號會等於被除數,也就是 % 的左側為正則出來的值也為正或零,% 左側為負則出來的值為負或零

  1. Modulo of negative integers in Go
  2. 模除

四則運算

在幾乎所有的語言中,都遵守先乘除後加減的四則運算,當你不確定運算優先序時 () 就直接給他加下去就對了

浮點數的運算

浮點數的運算基本上和整數差不多,但要注意一下,浮點數可以和直接整數(單純數字) 做運算,但不能和 整數變數(英文) 做運算。因為和純數字時Golang魔仗會直接幫你轉成浮點數。另一方面在Golang中取餘並不適用於浮點數。

package main
import "fmt"
func main(){
    m := 2.5
    m = m / 2
    fmt.Println(m)
}

執行結果
1.25

package main
import "fmt"
func main(){
    m := 2.5
    n := 2
    m = m / n
    fmt.Println(m)
}

那麼如果我真的這麼想要計算要怎麼辦?那麼就轉換型別吧!把浮點數轉成整數,或著把整數轉成浮點數

型別轉換

如果想把 int 轉成 float64 那麼就在要轉換的變數外加上 float64() 就可以了

package main
import "fmt"
func main(){
    m := 2.5
    n := 2
    m = m / float64(n)
    fmt.Println(m)
}

執行結果
1.25

這樣一來被套上 float64()的 n 就會以 float64的型態跟 m 做計算,要注意的事 n 仍然是 int 只有在套上時會變 float64(),也就是說「在宣告變數後」該變數只能就是這個型態了,型態並不能轉變。(有些語言並不這麼認為,比如php, javascript)

註解

幾乎所有的語言都有註解功能,加上註解,能幫助後續的維護,註解有相關的格式,但這邊先不做討論

在Golang牌中,用兩條斜線//來寫單行註解 ,// 後到換行前的所有字都會被忽略

package main
import "fmt"
func main(){
    m := 2.5    // m is float64
    n := 2      // f is int
    m = m / float64(n)
    fmt.Println(m)
}

如果要寫能換行的註解則是將你的註解包在 /* 與 */ 中

package main
import "fmt"
func main(){
    /* 
        這是一個
        可以跨行
        的註解
    */
    m := 2.5
    n := 2
    m = m / float64(n)
    fmt.Println(m)
}

大部份的語言都是採用 // 搭配 /**/ 的型式
python 因為把 // 拿去當做整數除法使用,所以是用 # 來做註解
Coffeescript 也是是 # 來做註解
NASM 則是以 ; 來當做註解

今日重點

  • 學習Golang牌中整數與浮點數的宣告及運算
  • 了解 uint 與 int 差別
  • 整數與浮點數間型別轉換
  • 變數的內容值可以變,但是型態不能變
  • 註解的使用

補充

宣告複數型態

如果是有讀過高中的朋友應該還記得什麼是複數吧!什麼?不記得了,標題就說文組不好說了,一般我們所使用的都是實數域的數,如果實數搭配虛數使用則稱複數,一般會用 a+bi 表示, a 為實部 b 為虛部,在Golang中用complex64或complex128來表示, 64 指的是 float32 的實部加上 float32 的虛部, 128 指的是 float64 的實部加上 float64 的虛部

package main
import "fmt"
func main(){
    var m complex64 = 1 + 2i
    var n complex64 = 3 + 4i
    var k complex128 = 5 + 10i

    fmt.Println(m + n)
    // fmt.Println(m + k) 報錯(型態不同)
    fmt.Println(k)
}

執行結果:
(4+6i)
(5+10i)

圖片來源(Go官網):The Go Programming Language Specification

附錄──不能當變數名稱

這些字用來保持Golang牌的運作,不能當變數名稱

後記

看來 2020 那時寫的幹話有點多,所以 2021 更新又刪減了一些

本文大多數圖片來自:庫洛魔法使第一季第二集


上一篇
#1 Golang魔法使──安裝與建置環境 | Golang魔法使
下一篇
#3 字串 ─ 知世:反正我們是屬於正義的一方 | Golang魔法使
系列文
Golang魔法使 ─ 30天從零開始學習Go語言 | 比Python還簡單 | 理科生一定學得會 | 文科生不好說 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言